﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Xunit;

namespace MetaSharp.Transformation.Tests.Patterns
{
	public class PropertyTests
	{
        [Fact]
        public async Task ObjectPatternWithFailedPropertyMatchDoesNotConsumeInput()
        {
            var p = Pattern.Object(
                typeof(object),
                Pattern.Property("y"));
            var m = await Pattern.Match(p, new { x = 7 });
            Assert.False(m.Matched);
        }

        [Fact]
		public async Task MatchesAnObjectAndASingleProperty()
		{
			var obj = new { A = 7 };
			var p = Pattern.Object(
					typeof(object),
					Pattern.Property("A"));
            var m = await Pattern.Match(p, obj);

            Assert.Equal(obj, m.Value);
		}

        [Fact]
		public async Task MatchesAnObjectPropertyAndValue()
		{
			var obj = new { A = 7 };
            var p = Pattern.Object(
                typeof(object),
                Pattern.Property("A", Pattern.Any()));
            var m = await Pattern.Match(p, obj);

			Assert.Equal(obj, m.Value);
		}

        [Fact]
        public async Task CanAccessPropertyValuesInProduction()
        {
            var p = Pattern.Production(
                Pattern.Object(
                    typeof(object),
                    Pattern.Property("A", Pattern.Variable("B", Pattern.Any()))),
                (int B) => B);
            var m = await Pattern.Match(p, new { A = 7 });

            Assert.Equal(7, m.Value);
        }

        [Fact]
		public async Task MatchesAnObjectAndOneOfTwoProperties()
		{
            var p = Pattern.Production(
                Pattern.Object(
                    typeof(object),
                    Pattern.Property("B", Pattern.Variable("b", Pattern.Any()))),
                (int b) => b);
            var m = await Pattern.Match(p, new { A = 7, B = 11 });

            Assert.Equal(11, m.Value);
		}

        [Fact]
        public async Task MatchesNestedObjectAndProperty()
        {
            var p = Pattern.Object(
                typeof(object),
                Pattern.Property("A",
                    Pattern.Object(
                        typeof(object),
                        Pattern.Property("B"))));
            var m = await Pattern.Match(p, new { A = new { B = 7 } });

            Assert.True(m.Matched);
		}

        [Fact]
		public async Task MatchesNestedArray()
		{
            var p = Pattern.Object(
                typeof(object),
                Pattern.Property("A", Pattern.Array(
                    Pattern.And(
                        Pattern.Any(),
                        Pattern.Any()))));
            var m = await Pattern.Match(p, new { A = new[] { 7, 11 } });

            Assert.True(m.Matched);
		}

        [Fact]
		public async Task CanRecursivelyDrillIntoObjects()
		{
            // R = object { a:A=R } -> a
            //   | int { };
            Pattern p = null;
			p = Pattern.Rule(
                Pattern.Or(
                    Pattern.Production(
				        Pattern.Object(
				            typeof(object),
				            Pattern.Property("A", Pattern.Variable("a",  Pattern.Reference(() => p)))),
                        (int a) => a),
                    Pattern.Object(typeof(int))));

            var m = await Pattern.Match(p, new { A = new { A = 7 } });

            Assert.True(m.Matched);
			Assert.Equal(7, m.Value);
		}
	}
}
